跳到主要内容

Topic 最佳实践

提前规划分区数(partitions)

Kafka 的分区本质是吞吐能力的扩展单位,每个分区只能被一个消费者线程独占,所以分区越多并发处理能力越高。

但分区太多会增加延迟、rebalance 时长、控制器压力、段文件数量……像齿轮咬得太密会卡住。

所以一次性把分区确定好,够用就行。增加分区会打乱消息顺序,消费者也会发生 rebalance 引发抖动。

如何估算合理的分区数,大致有如下两种方式:

根据消息 QPS(每秒条数)估算分区数

实际上,可以根据业界经验来快速估算分区数,这种是最简单快速的方式。

1.1. 每个分区基本上支持 2k–10k QPS。

  • 小消息(<1KB):每分区大概能处理 5k–10k QPS
  • 中消息(5–10KB):1k–5k QPS
  • 大消息(>50KB):< 1k QPS

根据这个数据,衡量下自己的业务 QPS 也能快速给出需要的分区数。

2.2. 根据服务并发数确定分区数

实际上,一个分区同一时刻只能被一个线程消费,根据这点也能快速确定需要的分区数。

比如:服务需要同时开 8 个消费者线程,那你的 partitions 至少得是 ≧ 8。

3.3. 根据关键服务确定分区数

因为每个消费者组独立消费,消费者组之间互相不影响。比如,如下几个服务同时订阅一个 topic,但是每个服务的重要程度不一,而且实例数也不相同:

订单服务有 8 个实例
库存服务有 4 个实例
风控服务有 6 个实例

每个服务都有自己的消费组,就各自独占整个 topic。

所以,只需要考虑最需要并发的那个组的并发能力。就是说最忙的订单服务想 10 个线程并发,那么 topic 至少需要 10 个分区。其他服务即使有 20 个实例,那只是竞争,但不会互抢分区。

根据数据量(吞吐 MB/s)估算分区数

需要的分区数 = 峰值总吞吐量(MB/s) / 单分区可承载吞吐(MB/s)

根据行业经验,单分区正常能跑 1–10MB/s,所以取中位数 6MB/s 就很安全。

比如:

  • 峰值每秒 50MB
  • 单分区吞吐估计 6MB/s
partitions = 50 / 6 ≈ 9

实际会取整提升一点:12 分区。

不过,百分之九十的业务场景根本达不到峰值 50MB/s,能达到这个量级的大多都是电商业务和金融场景。

Note

不过,就小公司而言。不管啥业务,3 个分区足以~

至少设置 3 个副本数(replication-factor)

副本数就是分区的备份数,至少 3 个副本才能防止数据高可用(前提是别只在同一台一起上设置副本数)。

行业准则:

  • prod 环境:至少 3 副本
  • 准生产:2 副本可接受
  • 不允许只有 1,消息丢得像漏水的桶

设置 min.insync.replicas 确保消息可靠性

副本数是“几份拷贝”,而 min.insync.replicas(ISR 最小同步副本数)是“至少写入几份才算成功”。

最佳实践组合:

  • replication.factor = 3
  • min.insync.replicas = 2
  • producer 设置:acks = all

这样,即使某个节点挂掉,消息也不会丢。

cleanup.policy:delete/compact 按用途选择

Kafka 的日志清理像是图书管理员的整理习惯。

  • deletedelete:按时间/容量删旧日志(默认,用于大多数业务)
  • compactcompact:按 key 保留最后一条(适用于用户状态、配置状态、KV 表等)
NOTE

如果你不知道 compact 是干什么的,那就无脑用 delete。

合理设置 retention.ms / retention.bytes

千万别让 topic 的 log 数据像雪球一样,越滚越大,一定要定时清理。

常见配置策略:

  • 高频业务事件(如订单事件):3–7 天
  • 日志类 topic:1–3 天
  • 需要追历史的事件溯源(如金融交易流水):30 天 或者用数据湖做归档

如果你需要几个月的日志数据,建议还是考虑 Elastic 吧

避免无限数量的小 topic

Kafka 不是为了存几千个 topic 设计的。每个 topic ✖️ 每个 partition ✖️ 每个 replica 都要对应一个 log segment 目录,这会让 broker “目录爆炸”。

行业经验:

  • 中小型集群:topic 控制在 200–500 以内
  • 大型集群:几千 topic 也行,但要有强壮机器与良好规划
  • 不要给每个用户/租户单独建一个 topic(多半会后悔)

一定要禁用自动创建 topic

每个 broker 在运行之前都要在 service.properties 增加如下设置,别问为什么,照做就完事了(这是经典的运维自保策略):

auto.create.topics.enable=false

topic 名字一定要有清晰的命名规范

名称里带上业务含义与版本信息,可以像考古层一样一眼看懂。

常见格式:

{业务域}.{事件名}[.版本]
order.order_paid
user.profile_updated.v1

保持 key 一致,不破坏分区路由

首先要记住:不同的分区,消息消费顺序是完全乱序的。

如果你想同一个订单的消息保持顺序,就永远用相同的 key,例如订单号。千万不要用随机 key,会打乱顺序。

生产者将消息投递到 topic 时,会根据 key 的 hash 值分配具体的分区。以订单消息为例,如果将消息 ID 固定为订单号。订单创建消息、支付成功消息、接单消息等都会落入同一个消息分区。

在同一个消息分区内,消息的消费顺序时一定的,这样就有效的避免了消息乱序问题。

反过来,如果消息 ID 使用随机 key。可能会出现订单创建消息落入 P1 分区,订单支付消息落入 P2 分区。

比如消息组A订阅订单创建消息。消费组B订阅订单支付成功消息。消息虽然是按照时间顺序写入各自的分区了,但是每个分区在吞吐量上可能存在差异,你没法保证消费组A消费的订单创建消息一定在消费组B之前。

不要频繁修改 topic 的关键配置

Kafka 的“双刃剑之一”就是过度灵活。分区数、retention 等都能改,但频繁修改会带来如下副作用:

  • rebalance 暴增
  • topic layout 改变造成延迟升高
  • 消费者逻辑依赖顺序时发生意外

能不改的尽量不要改,事前规划永远比事后修补好。

对关键 topic 开启监控与告警

至少监控:

  • lag(消费延迟)
  • 副本是否同步(UnderReplicatedPartitions)
  • 分区 leader 分配是否均衡
  • topic 大小

生产 Kafka 没监控就像开高铁但不看仪表盘。

对“无谓消息”避免发送

消息一旦进 topic 就会占用磁盘、带宽和 CPU,这不是免费的午餐。

策略包括:

  • 避免发送重复事件(可在生产端去重)
  • 避免发送超大 payload(>1MB)
  • 消息压缩:lz4/snappy 是行业常用

扩展:实际生产中的典型 topic 配置示例

订单事件示例:

partitions=12
replication.factor=3
min.insync.replicas=2
cleanup.policy=delete
retention.ms=604800000 ## 7天
compression.type=lz4

状态类 topic(KV 状态快照):

partitions=8
replication.factor=3
min.insync.replicas=2
cleanup.policy=compact